/**
 * 
 */
package com.oron3.bouncer.objects.shapes;



/**
 * @author Ofek
 *
 */
public class Plane implements Shape {
	public Vector n;
	public float d;
	public static final int RELATION_IN_NORAML_DIRECTION = 0;
	public static final int RELATION_IN_OPPOSITE_NORAML_DIRECTION=1;
	public static final int RELATION_IN_BOTH_DIRECTION = 3;
	public static final int RELATION_OVERLAP = 2;
	private static final float TOLERANCE = 0.00001f;

	/**
	 * NOTE :
	 * 
	 * @param p1
	 * @param p2
	 * @param p3
	 */
	public Plane(final Point p1, final Point p2, final Point p3) {
		final Vector v = new Vector(p1.sub(p2));
		final Vector u = new Vector(p3.sub(p2));
		if (v.isZero() || u.isZero()) throw new RuntimeException("points dont form a rect");
		n = new Vector(v).cross(u).normalize();
		final Vector vector = new Vector(p2);
		d = -n.dot(vector);

	}




	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.objects.GLObject#intersects(com.oron3.bouncer.objects.GLObject)
	 */
	@Override
	public boolean intersects(final Shape o) {
		if (o instanceof Sphere) {
			final Sphere s = (Sphere) o;
			final Point projection = (Point) project(s.center);
			return new Vector(s.center.sub(projection)).length() <= s.r;
		}
		throw new IllegalArgumentException();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.objects.shapes.Shape#intersection(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public Intersection intersection(final Shape o) {
		if ( o instanceof Line ) {

			final Line l = (Line) o;
			if (isInside(l.p1) && isInside(l.p1.add(l.d))) {
				return getIntersectionLineSegment(l);
			}
			final Vector vDirection = l.d;
			final Point p = l.p1;

			final Point project = (Point) project(p);
			if ( project==null ) return null;
			final Vector u = new Vector(project.sub(p));
			final Vector uDirection = u.normalize();
			final float cosa = vDirection.dot(uDirection);

			float vLength;
			if ( cosa==0 ) {
				final Point add = p.add(vDirection.mul(u.length()));
				return new Intersection(add);
			}
			if ( cosa<0 ) {
				vLength = u.length()/(-cosa);
				final Point add = p.add(vDirection.mul(-vLength));
				return new Intersection(add);
			}
			else {
				vLength = u.length()/(cosa);
				final Point add = p.add(vDirection.mul(vLength));
				return new Intersection(add);
			}
		}
		return null;
	}

	/**
	 * @param l
	 * @return
	 */
	private Intersection getIntersectionLineSegment(final Line l) {
		throw new IllegalArgumentException();
	}




	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oron3.bouncer.objects.shapes.Shape#calcRelation(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public int describeRelation(final Shape o) {
		if (o instanceof Sphere) {
			final Sphere s = (Sphere) o;
			final Vector mul = n.mul(s.r);
			final Point right = s.center.add(mul);
			final Point left = s.center.sub(mul);
			final int rightRelation = describeRelation(right);
			final int leftRelation = describeRelation(left);
			final boolean rinside = rightRelation == RELATION_IN_NORAML_DIRECTION || rightRelation == RELATION_OVERLAP;
			final boolean linside = leftRelation == RELATION_IN_NORAML_DIRECTION || leftRelation == RELATION_OVERLAP;
			if (rinside && linside) return RELATION_IN_NORAML_DIRECTION;
			if (!linside && !rinside) return RELATION_IN_OPPOSITE_NORAML_DIRECTION;
			return RELATION_IN_BOTH_DIRECTION;
		} else if (o instanceof Point) {
			final Point p = (Point) o;
			final float eq = n.dot(new Vector(p)) + d;
			if (Math.abs(eq)<= TOLERANCE) return RELATION_OVERLAP;
			if (eq < 0) return RELATION_IN_OPPOSITE_NORAML_DIRECTION;
			return RELATION_IN_NORAML_DIRECTION;
		}
		throw new IllegalArgumentException();
	}



	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#isInside(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public boolean isInside(final Shape o) {
		return describeRelation(o)==RELATION_OVERLAP;
	}

	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#project(com.oron3.bouncer.objects.shapes.Shape)
	 */
	@Override
	public Shape project(final Shape o) {
		if( o instanceof Point ) {
			final Point p = (Point) o;
			final Vector vector = new Vector(p);
			return p.sub(n.mul(n.dot(vector) + d));
		}
		throw new IllegalArgumentException();
	}




	/* (non-Javadoc)
	 * @see com.oron3.bouncer.objects.shapes.Shape#collision(com.oron3.bouncer.objects.shapes.Shape, com.oron3.bouncer.objects.shapes.Vector)
	 */
	@Override
	public Collision collision(final Shape shape, final Vector v) {
		final Vector nV = v.normalize();
		if ( shape instanceof Sphere ) {
			final int r = describeRelation(shape);
			if ( r==RELATION_IN_BOTH_DIRECTION ) throw new IllegalArgumentException();
			if ( r==RELATION_IN_NORAML_DIRECTION ) {

			}
		}
		throw new IllegalArgumentException();
	}
}
